package pl.edu.fuw.fft;

public class Complex {
	
	private final double re;   // the real part
    private final double im;   // the imaginary part

    public Complex(double real, double imag) {
        re = real;
        im = imag;
    }
    
    public Complex(Complex complex) {
		this.re = complex.re;
		this.im = complex.im;
	}

	public double abs()   { return Math.hypot(re, im); }  // Math.sqrt(re*re + im*im)
    public double phase() { return Math.atan2(im, re); }  // between -pi and pi
    
    
    public Complex add(Complex b) {
        Complex a = this;             // invoking object
        double real = a.re + b.re;
        double imag = a.im + b.im;
        return new Complex(real, imag);
    }

    public Complex subtract(Complex b) {
        Complex a = this;
        double real = a.re - b.re;
        double imag = a.im - b.im;
        return new Complex(real, imag);
    }

    public Complex multiplicate(Complex b) {
        Complex a = this;
        double real = a.re * b.re - a.im * b.im;
        double imag = a.re * b.im + a.im * b.re;
        return new Complex(real, imag);
    }

    public Complex multiplicate(double scalar) {
        return new Complex(scalar * re, scalar * im);
    }

    public Complex conjugate() {  return new Complex(re, -im); }


    public double re() { return re; }
    public double im() { return im; }
    
    public Complex getW(double k, int N) {
		return new Complex(Math.cos(k*2*Math.PI/N), - Math.sin(k*2*Math.PI/N));
	}

    public Complex exp() {
        return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im));
    }
    
    public static Complex[] conjugateAll(Complex[] vector){
    	Complex[] result = new Complex[vector.length];
    	for (int i = 0; i < vector.length; i++) {
            result[i] = vector[i].conjugate();            
        }
    	return result;
    }
    
	public static Complex[] divideAllByN(Complex[] vector) {
		int n = vector.length;
		Complex[] result = new Complex[n];
		for (int i = 0; i < n; i++) {
            result[i] = vector[i].multiplicate(1.0 / n);
        }
		return result;
	}
	
	public static Complex[] getArrayFromReals(double[] reals){
		int n = reals.length;
		Complex[] complex = new Complex[n];
		for (int i = 0; i < n; i++) {
			complex[i] = new Complex(reals[i], 0);
		}
		return complex;
	}
	
	public static double[] generateArrayOfReals(Complex[] complex){
		int n = complex.length;
		double[] reals = new double[n];
		for (int i = 0; i < n; i++) {
			reals[i] = complex[i].getRe();
		}
		return reals;
	}

	public double getRe() {
		return re;
	}

	public double getIm() {
		return im;
	}
	
	public static double[] getMagnitude(Complex[] signal) {
		int n = signal.length;
		double[] magnitude = new double[n];
		for (int i = 0; i < n; i++) {
			magnitude[i] = signal[i].abs();
		}
		return magnitude;
	}

}
